﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using GpgApi;

namespace WgpgCore
{
    public sealed class KeyNode : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private Key _key;
        private readonly ObservableCollection<KeyUserInfoNode> _userInfoNodes;
        private readonly ObservableCollection<KeyPhotoNode> _photoNodes;

        public KeyId Id { get { return _key.Id; } }
        public FingerPrint FingerPrint { get { return _key.FingerPrint; } }
        public KeyType Type { get { return _key.Type; } }
        public Boolean IsDisabled { get { return _key.IsDisabled; } }
        public UInt32 Size { get { return _key.Size; } }
        public KeyOwnerTrust OwnerTrust { get { return _key.OwnerTrust; } }
        public KeyTrust Trust { get { return _key.Trust; } }
        public DateTime CreationDate { get { return _key.CreationDate; } }
        public GpgDateTime ExpirationDate { get { return _key.ExpirationDate; } }
        public KeyAlgorithm Algorithm { get { return _key.Algorithm; } }

        public ExternalReadOnlyObservableCollection<KeyUserInfoNode> UserInfos { get; private set; }
        public ExternalReadOnlyObservableCollection<KeyPhotoNode> Photos { get; private set; }

        public String FingerPrintBeautified
        {
            get
            {
                if (FingerPrint == null)
                    return null;

                return Utils.GetFingerPrintBeautified(FingerPrint.ToString());
            }
        }

        public Boolean MatchEmail(Email email)
        {
            if (email == null)
                return false;

            return MatchEmail(new Email[] { email });
        }

        public Boolean MatchEmail(IList<Email> emails)
        {
            if (_userInfoNodes != null && emails != null)
            {
                foreach (KeyUserInfoNode userInfo in _userInfoNodes)
                {
                    if (userInfo.Email != null && emails.Contains(userInfo.Email))
                        return true;
                }
            }

            return false;
        }

        internal KeyNode(Key key)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            _key = key;
            _userInfoNodes = new ObservableCollection<KeyUserInfoNode>();
            _photoNodes = new ObservableCollection<KeyPhotoNode>();

            UserInfos = new ExternalReadOnlyObservableCollection<KeyUserInfoNode>(_userInfoNodes);
            Photos = new ExternalReadOnlyObservableCollection<KeyPhotoNode>(_photoNodes);

            SetUserInfos();
            SetPhotos();
        }

        private void NotifyPropertyChanged(String name)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

        internal void SetKey(Key key)
        {
            if (key == null)
                return;

            Boolean propertyKeyIdChanged = !KeyId.Equals(_key.Id, key.Id);
            Boolean propertyFingerPrintChanged = !FingerPrint.Equals(_key.FingerPrint, key.FingerPrint);
            Boolean propertyTypeChanged = !_key.Type.Equals(key.Type);
            Boolean propertyIsDisabledChanged = !_key.IsDisabled.Equals(key.IsDisabled);
            Boolean propertySizeChanged = !UInt32.Equals(_key.Size, key.Size);
            Boolean propertyOwnerTrustChanged = !_key.OwnerTrust.Equals(key.OwnerTrust);
            Boolean propertyTrustChanged = !_key.Trust.Equals(key.Trust);
            Boolean propertyCreationDateChanged = !DateTime.Equals(_key.CreationDate, key.CreationDate);
            Boolean propertyExpirationDateChanged = !GpgDateTime.Equals(_key.ExpirationDate, key.ExpirationDate);
            Boolean propertyAlgorithmChanged = !_key.Algorithm.Equals(key.Algorithm);

            _key = key;

            if (propertyKeyIdChanged) NotifyPropertyChanged("Id");
            if (propertyFingerPrintChanged) NotifyPropertyChanged("FingerPrint");
            if (propertyFingerPrintChanged) NotifyPropertyChanged("FingerPrintBeautified");
            if (propertyTypeChanged) NotifyPropertyChanged("Type");
            if (propertyIsDisabledChanged) NotifyPropertyChanged("IsDisabled");
            if (propertySizeChanged) NotifyPropertyChanged("Size");
            if (propertyOwnerTrustChanged) NotifyPropertyChanged("OwnerTrust");
            if (propertyTrustChanged) NotifyPropertyChanged("Trust");
            if (propertyCreationDateChanged) NotifyPropertyChanged("CreationDate");
            if (propertyExpirationDateChanged) NotifyPropertyChanged("ExpirationDate");
            if (propertyAlgorithmChanged) NotifyPropertyChanged("Algorithm");

            SetUserInfos();
            SetPhotos();
        }

        private KeyUserInfoNode GetKeyUserInfoNodeByIndex(UInt32 index)
        {
            return _userInfoNodes.Find(delegate(KeyUserInfoNode userInfo) { return userInfo.Index == index; });
        }

        private void SetUserInfos()
        {
            Boolean changed = false;
            List<KeyUserInfo> userInfos = (List<KeyUserInfo>)_key.UserInfos;

            foreach (KeyUserInfo userInfo in userInfos)
            {
                KeyUserInfoNode node = GetKeyUserInfoNodeByIndex(userInfo.Index);
                if (node == null)
                {
                    _userInfoNodes.Add(new KeyUserInfoNode(this, userInfo));
                    changed = true;
                }
                else
                    node.SetUserInfo(userInfo);
            }

            for (Int32 i = 0; i < _userInfoNodes.Count; )
            {
                if (userInfos.Exists(delegate(KeyUserInfo userInfo) { return userInfo.Index == _userInfoNodes[i].Index; }))
                    ++i;
                else
                {
                    _userInfoNodes.RemoveAt(i);
                    changed = true;
                }
            }

            if (changed)
                NotifyPropertyChanged("UserInfos");
        }

        private void SetPhotos()
        {
            Boolean changed = false;
            List<KeyPhoto> photos = (List<KeyPhoto>)_key.Photos;

            foreach (KeyPhoto photo in photos)
            {
                KeyPhotoNode node = _photoNodes.Find(delegate(KeyPhotoNode photoNode) { return photoNode.Index == photo.Index; });
                if (node == null)
                {
                    _photoNodes.Add(new KeyPhotoNode(this, photo));
                    changed = true;
                }
                else
                    node.SetPhoto(photo);
            }

            for (Int32 i = 0; i < _photoNodes.Count; )
            {
                if (photos.Exists(delegate(KeyPhoto photo) { return photo.Index == _photoNodes[i].Index; }))
                    ++i;
                else
                {
                    _photoNodes.RemoveAt(i);
                    changed = true;
                }
            }

            if (changed)
                NotifyPropertyChanged("Photos");
        }
    }
}
